﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml;
using System.IO;

namespace Viper
{
    public class method
    {
        public string name;
        public uint address;
        public uint callvector;
        public string parm;
        public bool Global = true;
    }
    class tryblock
    {
        public uint weretago;
        public uint catchaddress;
        public uint start;
    }
    struct varible
    {
        public int address;
        public string Name;
    }
    public class vClass
    {
        public Buffer Code = new Buffer(6400);
        public uint pos = 0;
        public static List<method> funcs = new List<method>();
        Stack<tryblock> trys = new Stack<tryblock>();
        List<tryblock> darealtrys = new List<tryblock>();
        public static List<method> Calls = new List<method>();
        public List<string> Fields = new List<string>();
        public uint globalpos = 0;
        List<string> UsingStatements = new List<string>();
        public string Name;
        public vClass(string className)
        {
            Name = className;
        }
        public byte[] generateByteCode()
        {
            List<byte> bytecode = new List<byte>();
            
            for (int i = 0; i < pos; i++)
            {
                bytecode.Add(Code.buffer[i]);
            }
            return bytecode.ToArray();
        }
        public bool isMainClass = false;
        public void DeclareString(string name)
        {
            Fields.Add(name);
        }
        public void Emit(byte opcode)
        {
            Code.Write(pos, opcode);
            pos++;
        }
        
        public void ReadMetaData()
        {
            string XML = this.MetaData;
            StreamWriter sw = new StreamWriter("temp.xml");
            sw.Write(XML);
            sw.Close();
            XmlReader reader = XmlReader.Create("temp.xml");
            try
            {
                while (reader.Read())
                {
                    if (reader.NodeType == XmlNodeType.Element
                        && reader.Name == "program")
                    {

                        while (reader.NodeType != XmlNodeType.EndElement)
                        {
                            //reader.Read();
                            if (reader.Name == "label")
                            {
                                method m = new method();
                                m.name = reader.GetAttribute(0);
                                m.address = uint.Parse(reader.GetAttribute(1));
                                funcs.Add(m);
                            }
                            reader.Read();
                        }

                    }

                } 
            }
            catch
            {
            }
            reader.Close();
            File.Delete("temp.xml");
        }
        public void Emit(byte opcode, byte Data)
        {
            Emit(opcode);
            Emit(Data);
        }
        public void Emit(byte opcode, string Data)
        {
            Code.Write(pos, opcode);
            pos++;
            Code.Write(pos,Data);
            pos += (uint)Data.Length;
            pos++;
        }
        public void Emit(string Data)
        {
            
            Code.Write(pos, Data);
            pos += (uint)Data.Length;
            pos++;
        }
        public void Emit(byte opcode, int data)
        {
            Emit(opcode);
            byte[] bits = System.BitConverter.GetBytes(data);
            foreach (byte b in bits)
            {
                Emit(b);
            }
        }
        public void Emit(int data)
        {
            byte[] bits = System.BitConverter.GetBytes(data);
            foreach (byte b in bits)
            {
                
                Emit(b);
            }
        }
        public void LabelStart(string name,bool Private = false)
        {
            method m = new method();
            m.address = pos;
            if (Private)
            {
                m.Global = false;
            }
            m.name = name;
            funcs.Add(m);
        }
        public void BeginTryBlock()
        {
            tryblock tb = new tryblock();
            Code.Write(pos, Viper.Opcodes.Try);

            pos++;
            tb.start = pos;
            pos += 4;
            trys.Push(tb);

        }
        public void Catch()
        {

            Code.Write(pos, Viper.Opcodes.Catch);
            pos++;
            trys.Peek().weretago = pos;
            pos += 4;
            Code.Write((uint)trys.Peek().start, (int)pos);


        }
        public void EndTry()
        {
            Code.Write((uint)trys.Peek().weretago, (int)pos);
            trys.Pop();
            pos++;
        }
        public uint CodeStart;
        public string MetaData = "";
        public void Jmp(string name, byte opcode,string Class = "")
        {
            Code.Write(pos, opcode);

            pos++;
            if (Class != "")
            {
                Code.Write(pos, Class);
                pos++;
                pos += (uint)Class.Length;
            }
            else
            {

            }
            foreach (method m in funcs)
            {
                if (m.name == name)
                {
                    Code.Write(pos, (int)m.address);
                    Console.Write(m.name + ":" + m.address.ToString());
                    Console.ReadLine();
                    pos += 4;
                    return;
                }
            }
            Code.Write(pos, (int)0);
            pos += 4;
            method mt = new method();
            mt.callvector = pos - 4;
            mt.name = name;
            mt.parm = this.Name;
            Calls.Add(mt);
        }
        public static List<method> prevFuncs = new List<method>();
          
        public void End()
        {
            MetaData += "<program>\n";
            prevFuncs = new List<method>();
            foreach (method mm in funcs)
            {
                if (true)
                {
                    if (!prevFuncs.Contains(mm))
                    {
                        if (mm.Global)
                            MetaData += "<label name = \"" + mm.name + "\" address = \"" + mm.address.ToString() + "\"/>\n";
                    }
                    prevFuncs.Add(mm);
                    continue;
                }
            }
            foreach (method m in Calls)
            {
                foreach (method mm in funcs)
                {
                    if (mm.name == m.name && m.parm == this.Name)
                    {
                        //  MetaData += "<method name = \"" + mm.name + "\" address = \"" + mm.address.ToString() + "\"/>\n";
                        Code.Write(m.callvector, (int)mm.address);
                        Console.WriteLine(m.callvector + " " + mm.address + mm.name);
                        continue;
                    }
                }
            }
            MetaData += "</program>";
        }
    }

}
